#include <iostream>
#include <string>
#include <vector>
#include "libnet.h"
#include "World.h"

#define NEWCHUNKS 10
#define MAJ 20

using namespace std;

// Liste de tous les clients connectés au serveur
struct ClientOnServer {
	SocketServer server_client;
	pthread_t cli_thread;
};

struct JavaPlugin {
	SocketServer javaClient;
	pthread_t waitJava;
};

// variables globales
SocketServer server;
SocketServer javaServer;
JavaPlugin jp;
World world("classic");

vector<ClientOnServer*> server_clients;
typedef vector<ClientOnServer*>::iterator client_iterator;

// Threads qui récupèrent les messages recus par les clients (sur le serveur)
void *readInput(void *arg)
{
	ClientOnServer *client = (ClientOnServer*)(arg);
	string line = "";
	while(line != "exit")
    {
		line = client->server_client.readLine();
		cout << line << endl;
		if(line == "exit")
		{
			client_iterator it;
			bool erased = false;
			for(it = server_clients.begin(); it != server_clients.end() && !erased; ++it)
				if((*it) == client)
				{
					delete client;
					it = server_clients.erase(it);
					erased = true;
				}
		}
    }
}

// Thread qui attend des connections de clients et les ajoutes à la liste
void *waitConnections(void *arg)
{
  while(1)
    {
      ClientOnServer *client = new ClientOnServer;
      client->server_client = server.accept_connection();
      // A mettre en thread pour optimiser
      pthread_create(&client->cli_thread,
		     NULL,
		     readInput,
		     (void*)(client));
      server_clients.push_back(client);
    }
}

void *handleJavaPlugin(void *arg)
{
	int port = *(int*)(arg);
	int status = -1;
	long nbChunks = 0, nbBlocks = 0;
	while(1)
    {
		jp.javaClient = javaServer.accept_connection();
		char msgType;
		cout << "The java plugin is connected" << endl;
		do
		{
			status = jp.javaClient.readMsg(&msgType, 1);
			if(status == 0) break;
			switch(msgType)
			{
				case 10:
				{
					status = jp.javaClient.read4(&nbChunks);
					if(status == 0 || nbChunks == 0) break;
					for(int i=0; i<nbChunks; i++)
					{
						status = jp.javaClient.read4(&nbBlocks);
						if(status == 0 || nbBlocks == 0) break;
						
						Chunk_t c;
						//c.blocks.resize(nbBlocks);
						status = jp.javaClient.read4(&c.x);
						status = jp.javaClient.read4(&c.z);
						for(int i=0; i<nbBlocks; i++)
						{
							Block_t b;
							status = jp.javaClient.read4(&b.x);
							status = jp.javaClient.read4(&b.y);
							status = jp.javaClient.read4(&b.z);
							status = jp.javaClient.read4(&b.id);
							c.blocks.push_back(b);
						}
						world.addChunk(c);
					}
					break;
				}
				case 20:
				{
					status = jp.javaClient.read4(&nbChunks);
					if(status == 0 || nbChunks == 0) break;
					for(int i=0; i<nbChunks; i++)
					{
						status = jp.javaClient.read4(&nbBlocks);
						if(status == 0 || nbBlocks == 0) break;
						
						Chunk_t c;
						//c.blocks.resize(nbBlocks);
						status = jp.javaClient.read4(&c.x);
						status = jp.javaClient.read4(&c.z);
						for(int i=0; i<nbBlocks; i++)
						{
							Block_t b;
							status = jp.javaClient.read4(&b.x);
							status = jp.javaClient.read4(&b.y);
							status = jp.javaClient.read4(&b.z);
							status = jp.javaClient.read4(&b.id);
							world.addBlock(c.x, c.z, b);
						}
					}
				}
			}
		} while(status != 0);
		
		cout << "The java plugin is disconnected with status " << status << endl;
		jp.javaClient.closeSock();
    }
    cout << "Aborting java thread" << endl;
}

void clean()
{
  client_iterator it;
  for(it = server_clients.begin(); it != server_clients.end(); ++it)
    {
      delete (*it);
      it = server_clients.erase(it);
    }
}

int main(int argc, char **argv)
{
  if(argc < 3)
    {
      cerr << "Error : A port is needed to start the server" << endl;
      return 0;
    }
	
	pthread_t waitCo;
	int portJava = atoi(argv[2]);

	server = SocketServer(atoi(argv[1]));
	javaServer = SocketServer(portJava);
  
	pthread_create(&jp.waitJava, NULL, handleJavaPlugin, &portJava);
	pthread_create(&waitCo, NULL, waitConnections, NULL);

	string line = "";
	while(line != "exit")
    {
		getline(cin, line);
    }

	javaServer.closeSock();
	server.closeSock();
	jp.javaClient.closeSock();
	
	pthread_cancel(jp.waitJava);

	clean();
	
	world.saveAll();
	world.clean();
  
	return 0;
}
